/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package compilador;



import compilador.pojo.Mensagem;
import compilador.pojo.MensagemErro;
import compilador.pojo.Operando;
import compilador.pojo.Simbolo;
import compilador.pojo.Token;
import compilador.util.ManipuladorDeArquivo;
import java.io.File;
import java.util.ArrayList;
import java.util.List;



/**
 *
 * @author H
 */
public class AnalisadorSintatico {


    private TabelaDeSimbolos tabelaDeSimbolos;
    private AnalisadorLexical lexico;
    private Token token;
    private Mensagem mensagem;
    private List<Token> expressaoParaPosOrdem;
    private String tipoDaExpressao;
    private String nomeArquivoGerar;
    private int linhaErro;
    private int enderecoDisponivel;
    private int rotulo, numeroVariaveisAlocar = 0;
    private ManipuladorDeArquivo arquivo;



    public AnalisadorSintatico(AnalisadorLexical lexico, String nomeArquivoGerar) {

        this.lexico = lexico;
        this.nomeArquivoGerar = nomeArquivoGerar;
        this.tabelaDeSimbolos = new TabelaDeSimbolos();
        this.expressaoParaPosOrdem = new ArrayList<>();
        this.tipoDaExpressao = "";
        this.arquivo = new ManipuladorDeArquivo(new File( this.nomeArquivoGerar + "_compilador.obj"));
        this.enderecoDisponivel = 0;
        this.rotulo = 1;
    }



    private boolean proximoToken() {

        Object obj = lexico.pegaToken();

        if (obj instanceof Token) {

            this.token = (Token) obj;
            return true;
        } else {

            this.mensagem = (Mensagem) obj;
            return false;
        }
    }



    private boolean igual(String palavra) {

        if (this.token.getSimbolo().equals(palavra)) {
            return true;
        } else {
            return false;
        }
    }



    private void geraMensagemErro(int tipoErro, String mensagem) {

        this.mensagem = new MensagemErro(tipoErro, this.lexico.getNumeroLinha(), mensagem);
    }



    private void gerarCodigo(String rotulo, String instrucao, String atrib1, String atrib2) {


        while (rotulo.length() < 5) {
            rotulo += " ";
        }
        while (instrucao.length() < 10) {
            instrucao += " ";
        }
        while (atrib1.length() < 4) {
            atrib1 += " ";
        }
        while (atrib2.length() < 4) {
            atrib2 += " ";
        }

        System.out.println(rotulo + instrucao + atrib1 + atrib2);

        if (instrucao.trim().equals("START")) {
            this.arquivo.insereTextoArquivo(rotulo + instrucao + atrib1 + atrib2);
        } else {
            this.arquivo.insereTextoArquivo("\n" + rotulo + instrucao + atrib1 + atrib2);
        }
    }



    public Mensagem analiseSintatica() {


        if (!this.proximoToken()) {
            return this.mensagem;
        }

        if (this.igual("Sprograma")) {

            this.gerarCodigo("", "START", "", "");

            if (!this.proximoToken()) {
                return this.mensagem;
            }

            if (this.igual("Sidentificador")) {


                this.tabelaDeSimbolos.insere_tabela(this.token.getLexema(), Simbolo.NOME_PROGRAMA, "", true, "", "");

                if (!this.proximoToken()) {
                    return this.mensagem;
                }

                if (this.igual("Sponto_virgula")) {

                    if (!this.analisa_bloco()) {
                        return this.mensagem;
                    }

/*
                    Integer espacoRemover = this.tabelaDeSimbolos.desempilha_tabela();
                    this.gerarCodigo("", "DALLOC", String.valueOf(this.enderecoDisponivel - espacoRemover), espacoRemover.toString());
                    this.enderecoDisponivel -= espacoRemover;
*/                  
                    if( this.enderecoDisponivel > 0 ){
                        
                        this.gerarCodigo("", "DALLOC", "0", String.valueOf(this.enderecoDisponivel));
                    }
                    
                    this.gerarCodigo("", "HLT", "", "");


                    if (this.igual("Sponto")) {
                        return new Mensagem("Compilação realizada com sucesso.");
                    } else {
                        this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado catactere '.' (ponto).");
                        return this.mensagem;
                    }
                } else {
                    this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado caractere ';' (ponto e virgula).");
                    return this.mensagem;
                }

            } else {
                this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado um identificador.");
                return this.mensagem;
            }

        } else {
            this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado palavra reservada programa.");
            return this.mensagem;
        }

    }



    private String getTipoIdentificador(Token token) {

        String tipo = "";

        if (token != null) {
            if ((token.getLexema().equals("Sinteiro") || token.getLexema().equals("Sbooleano"))) {
                tipo = token.getLexema();
            } else if (token.getSimbolo().equals("Snumero")) {
                tipo = "Sinteiro";
            } else if (token.getSimbolo().equals("Sverdadeiro") || token.getSimbolo().equals("Sfalso")) {
                tipo = "Sbooleano";
            } else {
                tipo = this.tabelaDeSimbolos.pesquisa_tipo_varfunc(token.getLexema());
            }
        }

        return tipo;
    }



    private boolean validaTiposExpressao() {


        Token tk;
        boolean operador = true;
        int indice = 0;
        List<Token> expressao = new ArrayList<>(this.expressaoParaPosOrdem);

        while (!this.expressaoParaPosOrdem.isEmpty() && operador) {

            tk = null;
            indice = indice > 0 ? indice : 0;
            operador = false;


            for (int i = indice; i < this.expressaoParaPosOrdem.size(); i++) {


                Token tkFor = this.expressaoParaPosOrdem.get(i);


                if (!tkFor.getSimbolo().equals("Sidentificador") && !tkFor.getSimbolo().equals("Snumero")
                        && !tkFor.getSimbolo().equals("Sverdadeiro") && !tkFor.getSimbolo().equals("Sfalso")
                        && !tkFor.getSimbolo().equals("Sinteiro") && !tkFor.getSimbolo().equals("Sbooleano")) {


                    tk = tkFor;
                    operador = true;
                    break;

                } else {

                    if (this.tabelaDeSimbolos.is_procedimento(tkFor.getLexema())) {

                        this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "Não é permitido um procedimento em uma expressão.");
                        return false;
                    }
                    switch (tkFor.getSimbolo()) {
                        case "Snumero":
                            this.gerarCodigo("", "LDC", tkFor.getLexema().trim(), "");
                            break;
                        case "Sverdadeiro":
                        case "Sfalso":
                            this.gerarCodigo("", "LDC", tkFor.getSimbolo().equals("Sverdadeiro") ? "1" : "0", "");
                            break;
                        default:
                            String enderecoMemoria = this.tabelaDeSimbolos.get_enderecoMemoria(tkFor.getLexema());
                            //CASO SEJA UMA FUNÇÃO, INSERE UM CALL PARA QUE A MESMA SEJA EXECUTADA...
                            if (this.tabelaDeSimbolos.is_funcao(tkFor.getLexema())) {
                                this.gerarCodigo("", "CALL", this.tabelaDeSimbolos.get_rotulo(tkFor.getLexema()), "");
                            }
                            this.gerarCodigo("", "LDV", enderecoMemoria, "");
                            break;
                    }


                    String tipoIdentificador = this.getTipoIdentificador(tkFor);
                    this.expressaoParaPosOrdem.set(indice, new Token(tipoIdentificador, tipoIdentificador));
                }

                indice++;
            }

            if (operador) {

                Token tokenAntesAnterior, tokenAnterior;
                String tipoAntesAnterior, tipoAnterior;



                switch (tk.getSimbolo()) {
                    case "Smais":
                    case "Smenos":
                    case "Smult":
                    case "Sdiv":


                        tokenAnterior = ((indice - 1) >= 0) ? this.expressaoParaPosOrdem.get(indice - 1) : null;
                        tokenAntesAnterior = ((indice - 2) >= 0) ? this.expressaoParaPosOrdem.get(indice - 2) : null;

                        tipoAnterior = tokenAnterior.getSimbolo();
                        tipoAntesAnterior = tokenAntesAnterior.getSimbolo();


                        if (tipoAntesAnterior.equals("Sinteiro")
                                && tipoAnterior.equals("Sinteiro")) {


                            this.expressaoParaPosOrdem.remove(indice);
                            this.expressaoParaPosOrdem.remove(indice - 1);
                            indice -= 1;


                        } else {

                            this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "Esperado identificador do tipo inteiro p/(+,-,*,/)");
                            return false;
                        }
                        break;
                    case "Smaior":
                    case "Smaiorig":
                    case "Smenor":
                    case "Smenorig":
                    case "Sigual":
                    case "Sdif":


                        tokenAnterior = ((indice - 1) >= 0) ? this.expressaoParaPosOrdem.get(indice - 1) : null;
                        tokenAntesAnterior = ((indice - 2) >= 0) ? this.expressaoParaPosOrdem.get(indice - 2) : null;

                        tipoAnterior = tokenAnterior.getSimbolo();
                        tipoAntesAnterior = tokenAntesAnterior.getSimbolo();

                        if (tipoAntesAnterior.equals("Sinteiro")
                                && tipoAnterior.equals("Sinteiro")) {

                            this.expressaoParaPosOrdem.set(indice, new Token("Sbooleano", "Sbooleano"));
                            this.expressaoParaPosOrdem.remove(indice - 1);
                            this.expressaoParaPosOrdem.remove(indice - 2);
                            indice -= 1;
                        } else {

                            this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "Esperado identificador do tipo inteiro p/(>,>=,<,<=,<>,=)");
                            return false;
                        }
                        break;
                    case "Se":
                    case "Sou":

                        tokenAnterior = ((indice - 1) >= 0) ? this.expressaoParaPosOrdem.get(indice - 1) : null;
                        tokenAntesAnterior = ((indice - 2) >= 0) ? this.expressaoParaPosOrdem.get(indice - 2) : null;

                        tipoAnterior = tokenAnterior.getSimbolo();
                        tipoAntesAnterior = tokenAntesAnterior.getSimbolo();

                        if (tipoAntesAnterior.equals("Sbooleano")
                                && tipoAnterior.equals("Sbooleano")) {


                            this.expressaoParaPosOrdem.remove(indice);
                            this.expressaoParaPosOrdem.remove(indice - 1);
                            indice -= 1;
                        } else {

                            this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "Esperado identificador do tipo booleano p/(e, ou)");
                            return false;
                        }
                        break;
                    case "Snao":

                        tokenAnterior = ((indice - 1) >= 0) ? this.expressaoParaPosOrdem.get(indice - 1) : null;
                        tipoAnterior = this.getTipoIdentificador(tokenAnterior);

                        if (tipoAnterior.equals("Sbooleano")) {


                            this.expressaoParaPosOrdem.remove(indice);
                            //indice -= 1;
                        } else {

                            this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "Esperado identificador do tipo booleano p/(nao)");
                            return false;
                        }
                        break;

                    case "UNARIO":

                        tokenAnterior = ((indice - 1) >= 0) ? this.expressaoParaPosOrdem.get(indice - 1) : null;
                        tipoAnterior = this.getTipoIdentificador(tokenAnterior);

                        if (tipoAnterior.equals("Sinteiro")) {

                            this.expressaoParaPosOrdem.remove(indice);

                        } else {

                            this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "Esperado identificador do tipo inteiro p/(+,- Unário)");
                            return false;
                        }

                        break;

                    default:
                        operador = false;
                }//switch

                boolean flag_gerarCodigo = true;

                if (tk.getSimbolo().equals("UNARIO") && tk.getLexema().trim().equals("+")) {
                    flag_gerarCodigo = false;
                }
                //GERAÇÃO DE CÓDIGO.
                if (flag_gerarCodigo) {
                    this.gerarCodigo("", this.get_instrucao_operando(tk.getSimbolo()), "", "");
                }

            }//if
        }//while
//}
        this.tipoDaExpressao = this.expressaoParaPosOrdem.get(0).getSimbolo();
        this.expressaoParaPosOrdem = expressao;

        /*
         System.out.println();
         System.out.println();

         System.out.print("TIPO = " + this.tipoDaExpressao + "\nEXPRESSÃO = " + expressao);

         System.out.println();
         System.out.println();
         */
        return true;
    }



    private String get_instrucao_operando(String simbolo) {

        switch (simbolo) {

            case "Smais":
                return "ADD";
            case "Smenos":
                return "SUB";
            case "Smult":
                return "MULT";
            case "Sdiv":
                return "DIVI";
            case "Smaior":
                return "CMA";
            case "Smaiorig":
                return "CMAQ";
            case "Smenor":
                return "CME";
            case "Smenorig":
                return "CMEQ";
            case "Sigual":
                return "CEQ";
            case "Sdif":
                return "CDIF";
            case "Se":
                return "AND";
            case "Sou":
                return "OR";
            case "Snao":
                return "NEG";
            case "UNARIO":
                return "INV";
        }

        return null;
    }



    private void geraPosOrdem() {

        List<Token> listaPosOrdem = new ArrayList<>();
        List<Operando> pilhaOperandos = new ArrayList<>();

        //System.out.print("EXPRESSAO = " );

        for (Token tk : this.expressaoParaPosOrdem) {
            //    System.out.print(tk.getLexema());

            if (tk.getSimbolo().equals("Sidentificador") || tk.getSimbolo().equals("Snumero")
                    || tk.getSimbolo().equals("Sverdadeiro") || tk.getSimbolo().equals("Sfalso")) {

                listaPosOrdem.add(tk);
            } else {

                if (pilhaOperandos.isEmpty() || tk.getSimbolo().equals("Sabre_parenteses")) {

                    pilhaOperandos.add(new Operando(tk));

                } else if (tk.getSimbolo().equals("Sfecha_parenteses")) {


                    int indice = 0;
                    if (!pilhaOperandos.isEmpty()) {
                        indice = pilhaOperandos.size() - 1;
                    }

                    while (!pilhaOperandos.get(indice).getToken().getSimbolo().equals("Sabre_parenteses") && indice >= 0) {

                        listaPosOrdem.add(pilhaOperandos.get(indice).getToken());
                        pilhaOperandos.remove(indice);
                        indice--;
                    }


                    if (indice >= 0) {
                        pilhaOperandos.remove(indice);
                    }

                } else {

                    Operando operando = new Operando(tk);
                    int indice = pilhaOperandos.size() >= 1 ? pilhaOperandos.size() - 1 : 0;

                    if (!pilhaOperandos.isEmpty() && pilhaOperandos.get(indice).getPrecedencia() >= operando.getPrecedencia()) {

                        while (!pilhaOperandos.isEmpty() && pilhaOperandos.get(indice).getPrecedencia() >= operando.getPrecedencia()) {

                            listaPosOrdem.add(pilhaOperandos.remove(indice).getToken());
                            indice--;
                        }

                        pilhaOperandos.add(new Operando(tk));

                    } else {

                        pilhaOperandos.add(operando);
                    }
                }
            }
        }



        while (!pilhaOperandos.isEmpty()) {

            listaPosOrdem.add(pilhaOperandos.remove(pilhaOperandos.size() - 1).getToken());

        }

        /*        System.out.println();
         System.out.print("POS-ORDEM := ");
         for (Token tk : listaPosOrdem) {
         System.out.print(tk.getLexema());
         }
         System.out.println();
         */
        this.expressaoParaPosOrdem = listaPosOrdem;
    }



    private boolean analisa_bloco() {

        if (!this.proximoToken()) {
            return false;
        }

        if (!this.analisa_et_variaveis()) {
            return false;
        }

        //GERAÇÃO DE CÓDIGO.
        if (numeroVariaveisAlocar > 0) {
            int posic = this.enderecoDisponivel - this.numeroVariaveisAlocar;
            this.gerarCodigo("", "ALLOC", String.valueOf(posic), String.valueOf(this.numeroVariaveisAlocar));
            this.numeroVariaveisAlocar = 0;
        }

        if (!this.analisa_subrotinas()) {
            return false;
        }

        if (!this.analisa_comandos()) {
            return false;
        }

        return true;

    }



    private boolean analisa_et_variaveis() {

        if (this.igual("Svar")) {

            if (!this.proximoToken()) {
                return false;
            }

            if (this.igual("Sidentificador")) {


                while (this.igual("Sidentificador")) {


                    if (!this.analisa_variaveis()) {
                        return false;
                    }

                    if (this.igual("Sponto_virgula")) {

                        if (!this.proximoToken()) {
                            return false;
                        }
                    } else {
                        this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado caractere ';'.");
                        return false;
                    }
                }
            } else {
                this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado identificador. 1");
                return false;

            }
        } else {
            //this.geraMensagemErro(MensagemErro.ERRO_SINTATICO,"Esperado palavra reservada 'var'.");
            //return false;
        }

        return true;
    }



    private boolean analisa_variaveis() {

        //int espacoAlocarVariavel = 0;
        do {

            if (this.igual("Sidentificador")) {


                //espacoAlocarVariavel++;

                if (this.tabelaDeSimbolos.pesquisa_duplicvar_tabela(this.token.getLexema())) {
                    this.geraMensagemErro(MensagemErro.ERRO_TABELA_SIMBOLO, "Variavel duplicada");
                    return false;
                }
                this.tabelaDeSimbolos.insere_tabela(this.token.getLexema(), Simbolo.NOME_VARIAVEL, "var", false, String.valueOf(this.enderecoDisponivel++), "");


                if (!this.proximoToken()) {
                    return false;
                }

                if (this.igual("Sdoispontos") || this.igual("Svirgula")) {

                    if (this.igual("Svirgula")) {

                        if (!this.proximoToken()) {
                            return false;
                        }

                        if (this.igual("Sdoispontos")) {
                            this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado identificador. 2");
                            return false;
                        }
                    }

                } else {

                    this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado caractere ',' (virgula) ou ':' (dois pontos).");
                    return false;
                }
            } else {

                this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado identificador. 3");
                return false;
            }

        } while (!this.igual("Sdoispontos"));

        /*
         this.gerarCodigo("", "ALLOC", String.valueOf(this.enderecoDisponivel), String.valueOf(espacoAlocarVariavel));
         this.enderecoDisponivel += espacoAlocarVariavel;
         */

        if (!this.proximoToken()) {
            return false;
        }

        if (!this.analisa_tipo()) {
            return false;
        }

        return true;
    }



    private boolean analisa_tipo() {

        if (!this.igual("Sinteiro") && !this.igual("Sbooleano")) {
            this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado tipo inteiro ou booleano.");
            return false;
        } else {

            int numeroVariaveis = this.tabelaDeSimbolos.coloca_tipo_tabela(this.token.getSimbolo());
            this.numeroVariaveisAlocar += numeroVariaveis;


            if (!this.proximoToken()) {
                return false;
            }
        }

        return true;
    }



    private boolean analisa_comandos() {

        if (this.igual("Sinicio")) {

            if (!this.proximoToken()) {
                return false;
            }

            if (!this.analisa_comando_simples()) {
                return false;
            }

            while (!this.igual("Sfim")) {

                if (this.igual("Sponto_virgula")) {

                    if (!this.proximoToken()) {
                        return false;
                    }

                    if (!this.igual("Sfim")) {

                        if (!this.analisa_comando_simples()) {
                            return false;
                        }
                    }

                } else {

                    this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado caractere ';'.");
                    return false;
                }
            }

            if (!this.proximoToken()) {
                return false;
            }

        } else {
            this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado palavra reservada 'inicio'");
            return false;
        }

        return true;
    }



    private boolean analisa_comando_simples() {


        if (this.igual("Sidentificador")) {

            if (!this.analisa_atrib_chprocedimento()) {
                return false;
            }
        } else if (this.igual("Sse")) {

            if (!this.analisa_se()) {
                return false;
            }
        } else if (this.igual("Senquanto")) {

            if (!this.analisa_enquanto()) {
                return false;
            }
        } else if (this.igual("Sleia")) {

            if (!this.analisa_leia()) {
                return false;
            }
        } else if (this.igual("Sescreva")) {

            if (!this.analisa_escreva()) {
                return false;
            }
        } else {
            if (!this.analisa_comandos()) {
                return false;
            }
        }

        return true;
    }



    private boolean analisa_atrib_chprocedimento() {


        String tipoIdentificadorAtrib = this.getTipoIdentificador(this.token),
                identificador = this.token.getLexema();


        if (!this.proximoToken()) {
            return false;
        }


        if (this.igual("Satribuicao")) {


            if (!this.tabelaDeSimbolos.pesquisa_declvarfunc_tabela(identificador)) {
                this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "Identificador não declarado ou não é uma Variável/Função.");
                return false;
            }

            if (this.tabelaDeSimbolos.is_funcao(identificador) && !this.tabelaDeSimbolos.is_corpo_funcao(identificador)) {
                this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "Atribuicao pra função permitida apenas dentro do seu corpo.");
                return false;
            }


            if (!this.analisa_atribuicao()) {
                return false;
            }

            if (!tipoIdentificadorAtrib.equals(this.tipoDaExpressao)) {

                this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "Tipos imcompatíveis em atribuição. " + tipoIdentificadorAtrib + " := " + this.tipoDaExpressao);
                return false;
            }

            String endereco = this.tabelaDeSimbolos.get_enderecoMemoria(identificador);
            this.gerarCodigo("", "STR", endereco.trim(), "");



        } else {


            if (!this.tabelaDeSimbolos.pesquisa_declproc_tabela(identificador) && !this.tabelaDeSimbolos.pesquisa_declfunc_tabela(identificador)) {

                this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "Esperado um procedimento/função.");
                return false;
            }

            if (!this.analisa_chamada_procedimento(identificador)) {
                return false;
            }
        }

        return true;
    }



    private boolean analisa_leia() {

        if (!this.proximoToken()) {
            return false;
        }

        if (this.igual("Sabre_parenteses")) {

            if (!this.proximoToken()) {
                return false;
            }

            if (this.igual("Sidentificador")) {


                if (!this.tabelaDeSimbolos.is_variavel(this.token.getLexema())
                        || !this.tabelaDeSimbolos.pesquisa_tipo_varfunc(this.token.getLexema()).equals("Sinteiro")) {

                    this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "Identificador não declarado ou não é uma variável ou não é do tipo inteiro.");
                    return false;
                }


                this.gerarCodigo("", "RD", "", "");
                this.gerarCodigo("", "STR", this.tabelaDeSimbolos.get_enderecoMemoria(this.token.getLexema()), "");


                if (!this.proximoToken()) {
                    return false;
                }

                if (this.igual("Sfecha_parenteses")) {

                    if (!this.proximoToken()) {
                        return false;
                    }
                } else {

                    this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado caractere ')'");
                    return false;
                }
            } else {
                this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado identificador. 4");
                return false;
            }
        } else {

            this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado caractere '('");
            return false;
        }

        return true;
    }



    private boolean analisa_escreva() {


        if (!this.proximoToken()) {
            return false;
        }

        if (this.igual("Sabre_parenteses")) {

            if (!this.proximoToken()) {
                return false;
            }

            if (this.igual("Sidentificador")) {


                if (!this.tabelaDeSimbolos.pesquisa_declvarfunc_tabela(this.token.getLexema())) {
                    this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "Identificador não declarado ou não é Variável/Função");
                    return false;
                }


                if (!this.tabelaDeSimbolos.pesquisa_tipo_varfunc(this.token.getLexema()).equals("Sinteiro")) {
                    this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "É permitido escrever apenas identificadores do tipo Inteiro.");
                    return false;
                }


                String identificador = this.token.getLexema();
                if (!this.tabelaDeSimbolos.is_variavel(identificador)) {

                    this.gerarCodigo("", "CALL", this.tabelaDeSimbolos.get_rotulo(identificador), "");
                }

                this.gerarCodigo("", "LDV", this.tabelaDeSimbolos.get_enderecoMemoria(this.token.getLexema()), "");
                this.gerarCodigo("", "PRN", "", "");



                if (!this.proximoToken()) {
                    return false;
                }

                if (this.igual("Sfecha_parenteses")) {

                    if (!this.proximoToken()) {
                        return false;
                    }
                } else {
                    this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado caractere ')'");
                    return false;
                }

            } else {
                this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado identificador. 5");
                return false;
            }
        } else {

            this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado caractere '('");
            return false;
        }

        return true;
    }



    private boolean analisa_enquanto() {


        String auxRotuloEnquanto = "R" + String.valueOf(this.rotulo++);
        this.gerarCodigo(auxRotuloEnquanto.toString(), "NULL", "", "");


        if (!this.proximoToken()) {
            return false;
        }

        this.expressaoParaPosOrdem = new ArrayList<>();
        if (!this.analisa_expressao()) {
            return false;
        }
        this.geraPosOrdem();
        if (!this.validaTiposExpressao()) {

            //this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "Tipo de identificador incorreto em expressão.");
            return false;
        }

        if (!this.tipoDaExpressao.equals("Sbooleano")) {

            this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "O resultado da expressão dentro de um comando ENQUANTO deve ser do tipo booleano.");
            return false;
        }

        String auxRotuloFim = "R" + String.valueOf(this.rotulo++);
        this.gerarCodigo("", "JMPF", auxRotuloFim.toString(), "");

        if (this.igual("Sfaca")) {

            if (!this.proximoToken()) {
                return false;
            }
            if (!this.analisa_comando_simples()) {
                return false;
            }


            this.gerarCodigo("", "JMP", auxRotuloEnquanto.toString(), "");
            this.gerarCodigo(auxRotuloFim.toString(), "NULL", "", "");

        } else {

            this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado palavra reservada 'faca'");
            return false;
        }

        return true;
    }



    private boolean analisa_se() {

        if (!this.proximoToken()) {
            return false;
        }

        this.linhaErro = this.lexico.getNumeroLinha();
        this.expressaoParaPosOrdem = new ArrayList<>();
        if (!this.analisa_expressao()) {
            return false;
        }
        this.geraPosOrdem();
        if (!this.validaTiposExpressao()) {

            //this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "Tipo de identificador incorreto em expressão.");
            return false;
        }

        if (!this.tipoDaExpressao.equals("Sbooleano")) {

            this.lexico.setNumeroLinha(linhaErro);
            this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "O resultado da expressão dentro de um comando SE deve ser do tipo booleano.");
            return false;
        }

        String auxRotuloSenao = "R" + String.valueOf(this.rotulo++);
        this.gerarCodigo("", "JMPF", auxRotuloSenao.toString(), "");

        if (this.igual("Sentao")) {

            if (!this.proximoToken()) {
                return false;
            }

            if (!this.analisa_comando_simples()) {
                return false;
            }


            String auxRotuloFim = "R" + String.valueOf(this.rotulo++);
            this.gerarCodigo("", "JMP", auxRotuloFim.toString(), "");


            this.gerarCodigo(auxRotuloSenao.toString(), "NULL", "", "");

            if (this.igual("Ssenao")) {


                if (!this.proximoToken()) {
                    return false;
                }
                if (!this.analisa_comando_simples()) {
                    return false;
                }

            }

            this.gerarCodigo(auxRotuloFim.toString(), "NULL", "", "");

        } else {
            this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado palavra reservada 'entao'");
            return false;
        }

        return true;
    }



    private boolean analisa_subrotinas() {


        String auxRotulo = "", enderecoDisponivelFuncao = "";
        int flag = 0;



        if (this.igual("Sprocedimento") || this.igual("Sfuncao")) {
            auxRotulo = "R" + String.valueOf(this.rotulo++);

            if (this.igual("Sfuncao")) {

                enderecoDisponivelFuncao = String.valueOf(this.enderecoDisponivel++);
                this.gerarCodigo("", "ALLOC", enderecoDisponivelFuncao, "1");
            }

            this.gerarCodigo("", "JMP", auxRotulo.toString(), "");
            flag = 1;
        }


        while (this.igual("Sprocedimento") || this.igual("Sfuncao")) {


            if (this.igual("Sprocedimento")) {
                if (!this.analisa_declaracao_procedimento()) {
                    return false;
                }
            } else {
                if (!this.analisa_declaracao_funcao(enderecoDisponivelFuncao)) {
                    return false;
                }
            }

            if (this.igual("Sponto_virgula")) {

                if (!this.proximoToken()) {
                    return false;
                }
            } else {

                this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado caractere ';'");
                return false;
            }
        }

        if (flag == 1) {
            this.gerarCodigo(auxRotulo.toString(), "NULL", "", "");
        }

        return true;
    }



    private boolean analisa_declaracao_procedimento() {

        if (!this.proximoToken()) {
            return false;
        }

        if (this.igual("Sidentificador")) {


            if (this.tabelaDeSimbolos.pesquisa_declproc_tabela(this.token.getLexema())) {
                this.geraMensagemErro(MensagemErro.ERRO_TABELA_SIMBOLO, "Procedimento/Função já declarado anteriormente.");
                return false;
            }

            String auxRotulo = "R" + String.valueOf(this.rotulo++);
            this.tabelaDeSimbolos.insere_tabela(this.token.getLexema(), Simbolo.NOME_PROCEDIMENTO, "", true, "", String.valueOf(auxRotulo));
            this.gerarCodigo(auxRotulo.toString(), "NULL", "", "");

            if (!this.proximoToken()) {
                return false;
            }

            if (this.igual("Sponto_virgula")) {

                if (!this.analisa_bloco()) {
                    return false;
                }
            } else {

                this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado caractere ';'");
                return false;
            }
        } else {

            this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado identificador. 6");
            return false;
        }


        Integer espacoRemover = this.tabelaDeSimbolos.desempilha_tabela();

        if (espacoRemover > 0) {
            this.gerarCodigo("", "DALLOC", String.valueOf(this.enderecoDisponivel - espacoRemover), espacoRemover.toString());
            this.enderecoDisponivel -= espacoRemover;
        }


        this.gerarCodigo("", "RETURN", "", "");
        return true;
    }



    private boolean analisa_declaracao_funcao(String enderecoDisponivel) {




        if (!this.proximoToken()) {
            return false;
        }


        String nomeFuncao = this.token.getLexema();

        if (this.igual("Sidentificador")) {


            if (this.tabelaDeSimbolos.pesquisa_declfunc_tabela(this.token.getLexema())) {
                this.geraMensagemErro(MensagemErro.ERRO_TABELA_SIMBOLO, "Função/Procedimento Já declarado.");
                return false;
            }

            String auxRotulo = "R" + String.valueOf(this.rotulo++);

            this.tabelaDeSimbolos.insere_tabela(this.token.getLexema(), Simbolo.NOME_FUNCAO, "var", true, enderecoDisponivel, auxRotulo.toString());
            this.gerarCodigo(auxRotulo.toString(), "NULL", "", "");



            if (!this.proximoToken()) {
                return false;
            }

            if (this.igual("Sdoispontos")) {

                if (!this.proximoToken()) {
                    return false;
                }

                if (this.igual("Sinteiro") || this.igual("Sbooleano")) {


                    this.tabelaDeSimbolos.coloca_tipo_tabela(this.token.getSimbolo());

                    if (!this.proximoToken()) {
                        return false;
                    }

                    if (this.igual("Sponto_virgula")) {

                        if (!this.analisa_bloco()) {
                            return false;
                        }
                    } else {
                        this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado caractere ';'");
                        return false;
                    }
                } else {
                    this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado tipo 'inteiro' ou 'booleano'");
                    return false;
                }
            } else {

                this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado caractere ':'");
                return false;
            }

        } else {

            this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado identificador. 7");
            return false;
        }


        Integer espacoRemover = this.tabelaDeSimbolos.desempilha_tabela();

        if (espacoRemover > 0) {
            this.gerarCodigo("", "DALLOC", String.valueOf(this.enderecoDisponivel - espacoRemover), espacoRemover.toString());
            this.enderecoDisponivel -= espacoRemover;
        }

        //this.gerarCodigo("", "DALLOC", String.valueOf(this.enderecoDisponivel - 1), "1");
        //this.enderecoDisponivel -= espacoRemover;

        this.gerarCodigo("", "RETURN", "", "");

        return true;
    }



    private boolean analisa_expressao() {



        if (!this.analisa_expressao_simples()) {
            return false;
        }

        if (this.igual("Smaior") || this.igual("Smaiorig") || this.igual("Smenor") || this.igual("Smenorig") || this.igual("Sdif") || this.igual("Sigual")) {

            this.expressaoParaPosOrdem.add(this.token);
            if (!this.proximoToken()) {
                return false;
            }

            if (!this.analisa_expressao_simples()) {
                return false;
            }
        }

        return true;
    }



    private boolean analisa_expressao_simples() {

        if (this.igual("Smais") || this.igual("Smenos")) {

            //System.out.println(this.token.getLexema());
            this.expressaoParaPosOrdem.add(new Token(this.token.getLexema(), "UNARIO"));
            if (!this.proximoToken()) {
                return false;
            }
        }

        if (!this.analisa_termo()) {
            return false;
        }

        while (this.igual("Smais") || this.igual("Smenos") || this.igual("Sou")) {


            this.expressaoParaPosOrdem.add(this.token);
            if (!this.proximoToken()) {
                return false;
            }


            if (!this.analisa_termo()) {
                return false;
            }

        }


        return true;
    }



    private boolean analisa_termo() {

        if (!this.analisa_fator()) {
            return false;
        }

        while (this.igual("Smult") || this.igual("Sdiv") || this.igual("Se")) {

            this.expressaoParaPosOrdem.add(this.token);
            if (!this.proximoToken()) {
                return false;
            }

            if (!this.analisa_fator()) {
                return false;
            }
        }

        return true;
    }



    private boolean analisa_fator() {


        this.expressaoParaPosOrdem.add(this.token);
        if (this.igual("Sidentificador")) {

            int ind = this.tabelaDeSimbolos.pesquisa_tabela(this.token.getLexema(), null);
            if (ind >= 0) {

                if (this.tabelaDeSimbolos.is_funcao(ind)) {

                    if (!this.analisa_chamada_funcao()) {
                        return false;
                    }
                } else {

                    if (!this.proximoToken()) {
                        return false;
                    }
                }

            } else {
                this.geraMensagemErro(MensagemErro.ERRO_TABELA_SIMBOLO, "Identificador não encontrado na tabela de simbolos." + token.toString());
                return false;
            }

        } else if (this.igual("Snumero")) {

            if (!this.proximoToken()) {
                return false;
            }
        } else if (this.igual("Snao")) {

            if (!this.proximoToken()) {
                return false;
            }

            if (!this.analisa_fator()) {
                return false;
            }
        } else if (this.igual("Sabre_parenteses")) {

            if (!this.proximoToken()) {
                return false;
            }

            if (!this.analisa_expressao()) {
                return false;
            }

            if (this.igual("Sfecha_parenteses")) {

                this.expressaoParaPosOrdem.add(this.token);
                if (!this.proximoToken()) {
                    return false;
                }
            } else {
                this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado caractere ')'");
                return false;
            }
        } else if (this.igual("Sverdadeiro") || this.igual("Sfalso")) {

            if (!this.proximoToken()) {
                return false;
            }

        } else {

            this.geraMensagemErro(MensagemErro.ERRO_SINTATICO, "Esperado 'identificador', 'numero', 'nao', '(', 'verdadeiro' ou 'falso'.");
            return false;
        }

        return true;
    }



    private boolean analisa_atribuicao() {

        if (!this.proximoToken()) {
            return false;
        }


        this.expressaoParaPosOrdem = new ArrayList<>();
        if (!this.analisa_expressao()) {
            return false;
        }
        this.geraPosOrdem();
        if (!this.validaTiposExpressao()) {

            //this.geraMensagemErro(MensagemErro.ERRO_SEMANTICO, "Tipo de identificador incorreto em expressão.");
            return false;
        }

//        } else {
//
//            this.geraMensagemErro(MensagemErro.ERRO_SINTATICO,"Esperado identificador. 8");
//            return false;
//        }

        return true;
    }



    private boolean analisa_chamada_procedimento(String identificador) {


        String rotuloCall = this.tabelaDeSimbolos.get_rotulo(identificador);
        this.gerarCodigo("", "CALL", rotuloCall, "");


//        if (!this.proximoToken()) {
//            return false;
//        }


        return true;
    }



    private boolean analisa_chamada_funcao() {


        //String rotuloCall = this.tabelaDeSimbolos.get_rotulo(this.token.getLexema());
        //this.gerarCodigo("", "CALL", rotuloCall, "");

        if (!this.proximoToken()) {
            return false;
        }
        return true;
    }
}